#include "UnityPrefix.h"
#include "Wind.h"
#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
#include "Runtime/Input/TimeManager.h"
#include "Runtime/Graphics/Transform.h"
#include "Runtime/Geometry/AABB.h"
#include "Runtime/Math/Vector4.h"

// --------------------------------------------------------------------------


WindZone::WindZone (MemLabelId label, ObjectCreationMode mode)
:	Super (label, mode)
,	m_Mode (Directional)
,	m_Radius (20)
,	m_WindMain (1)
,	m_WindTurbulence (1)
,	m_WindPulseMagnitude (0.5f)
,	m_WindPulseFrequency (0.01f)
,	m_Node (this)
{
}


WindZone::~WindZone ()
{
}


template<class TransferFunc>
void WindZone::Transfer (TransferFunc& transfer)
{
	Super::Transfer (transfer);
	
	TRANSFER_ENUM(m_Mode);
	
	transfer.Transfer (m_Radius, "m_Radius");
	
	transfer.Transfer (m_WindMain, "m_WindMain");
	transfer.Transfer (m_WindTurbulence, "m_WindTurbulence");
	
	transfer.Transfer (m_WindPulseMagnitude, "m_WindPulseMagnitude");
	transfer.Transfer (m_WindPulseFrequency, "m_WindPulseFrequency");
}


Vector4f WindZone::ComputeWindForce (const AABB& bounds, float time) const
{
	// use point mid way between center and top center, as leaves are likely to be placed there
	Vector3f center = bounds.GetCenter ();
	// TODO : could use GetExtent instead
	center.y += (bounds.GetMax ().y - bounds.GetMin ().y) * 0.25f;
	
	float windPhase = time * kPI * m_WindPulseFrequency;
	float phase = windPhase + center.x * 0.1f + center.z * 0.1f;
	float pulse = (cos (phase) + cos (phase * 0.375f) + cos (phase * 0.05f)) * 0.333f;
	pulse = 1.0f + (pulse * m_WindPulseMagnitude);
	
	const Transform& transform = GetComponent (Transform);
	const Vector3f position = transform.GetPosition ();		
	
	if (m_Mode == Directional)
	{
		// Directional
		float power = pulse;
		
		// TODO : could do this when m_direction is set
		Vector3f delta = transform.TransformDirection (Vector3f::zAxis);
		delta = Normalize (delta);
		
		return Vector4f (
			delta.x * m_WindMain * power,
			delta.y * m_WindMain * power,
			delta.z * m_WindMain * power,
			m_WindTurbulence * power);
	}
	else
	{
		// Spherical with falloff
		float power = 1.0f - CalculateSqrDistance (position, bounds) / (m_Radius * m_Radius);
		if (power > 0.0f)
		{
			power *= pulse;
			
			Vector3f delta = center - position;
			delta = Normalize (delta);
			return Vector4f(
				delta.x * m_WindMain * power,
				delta.y * m_WindMain * power,
				delta.z * m_WindMain * power,
				m_WindTurbulence * power);
		}
	}
	
	return Vector4f(0,0,0,0);
}



// --------------------------------------------------------------------------


void WindZone::AddToManager ()
{
	WindManager::GetInstance ().AddWindZone (m_Node);
}


void WindZone::RemoveFromManager ()
{
	m_Node.RemoveFromList();
}

IMPLEMENT_CLASS(WindZone)
IMPLEMENT_OBJECT_SERIALIZE(WindZone)

// --------------------------------------------------------------------------

// Empty destructor so it won't be autogenerated all over the place
WindManager::~WindManager()
{
}

// No need to allocate this dynamically, it's a tiny class
WindManager WindManager::s_WindManager;

WindManager& WindManager::GetInstance ()
{
	return s_WindManager;
}

WindManager::WindZoneList& WindManager::GetList ()
{
	return m_WindZones;
}


Vector4f WindManager::ComputeWindForce (const AABB& bounds)
{
	float time = GetTimeManager ().GetTimeSinceLevelLoad ();	

	Vector4f force (0, 0, 0, 0);
	for (WindZoneList::iterator it = m_WindZones.begin (); it != m_WindZones.end (); ++it)
	{
		const WindZone& zone = **it;
		force = force + zone.ComputeWindForce (bounds, time);
	}
	return force;
}
